<?php
/**
 * User_Auth
 *
 * Класс User_Auth осуществляет работу с авторизацией и всем с этим связааным
 *
 * @author Dmitri Kouzma (kouzma.d@gmail.com)
 */
class User_Auth {
  
	private $md5Pass = null;
  private $pass = null;
  private $loginOrEMail = null;
  private $saveMe = true;
  private $needApprove = false;
  private $superSlovo = SECRET_WORD;
  private $kaptcha = null;
  private $checkKaptcha = true;
  private $useLogin = false;
  private $login = null;
  private $eMail = null;
  private static $arrBlockedUsers = array(); // Пользователи, которых мы заблокировали
  
  private $makeSignInSuccess = true; //залогинить пользователя в случае успеха, или нет
  
  /**
   * Конструктор
   * 
   * @param string $loginOrEMail - логин
   * @param string $pass - пароль (НЕ md5)
   */
  public function __construct($loginOrEMail = null, $pass = null) {
  	if (!empty($loginOrEMail)) $this->setLoginOrEmail($loginOrEMail);
  	if (!empty($pass)) $this->setPass($pass);
  }
  
  /**
   * Устанавливает значение логина или электронной почты
   * 
   * @param string $loginOrEMail
   */
  public function setLoginOrEmail($loginOrEMail) {
  	$this->loginOrEMail = $loginOrEMail;
  }  
  
  /**
   * Устанавливает значение логина
   * 
   * @param string $login
   */
  public function setLogin($login) {
  	$this->login = $login;
  }  
  
  /**
   * Устанавливает значение эл. почты
   * 
   * @param string $eMail
   */
  public function setEMail($eMail) {
  	$this->eMail = $eMail;
  }  
  
  /**
   * Устанавливает значение пароля
   * 
   * @param string $pass
   */
  public function setPass($pass) {
  	$this->pass = Service_Escape::absolutelyEscapeString($pass);
  	$this->md5Pass = md5($pass);
  }  
  
  /**
   * Устанавливает md5 значение пароля
   * 
   * @param string $md5Pass
   */
  public function setMd5Pass($md5Pass) {
  	$this->md5Pass = $md5Pass;
  	$this->pass = null;
  }
    
  /**
   * Устанавливает значение для SaveMe
   * 
   * @param string $saveMe
   */
  public function setSaveMe($saveMe) {
  	$this->saveMe = $saveMe;
  }
    
  /**
   * Устанавливает значение для $kaptcha
   * 
   * @param string $kaptcha
   */
  public function setKaptcha($kaptcha) {
  	$this->kaptcha = $kaptcha;
  }
  
  /**
   * Устанавливает флаг, нужно ли проверять каптчу
   * 
   * @param bool $ckeckKaptcha
   */
  public function setCheckKaptcha($ckeckKaptcha) {
  	$this->checkKaptcha = $ckeckKaptcha;
  }
  /**
   * Устанавливает значение для $makeSignInSuccess
   * 
   * @param bool $makeSignInSuccess
   */
  public function setMakeSignInSuccess($makeSignInSuccess) {
  	$this->makeSignInSuccess = $makeSignInSuccess;
  }
  

  
  
  
	/**
	 * Осуществялет вход в систему
	 * 
	 * @return $userId
	 */
	public function signIn() {
		
		if (empty($this->loginOrEMail) && empty($this->md5Pass)) throw new Exception(Service_Translate::getText('Не переданы данные'));
		if (empty($this->loginOrEMail)) throw new Exception(Service_Translate::getText('Не заполнен логин'));
		if (empty($this->md5Pass)) throw new Exception(Service_Translate::getText('Не заполнен пароль'));
		if (is_numeric($this->loginOrEMail)) {
			$userId = $this->_signInByBooking(); // Пробуем залогиниться через booking + pin
			if ($userId) return $userId;
		}
		if (Service_Mail::isEMailCorrect($this->loginOrEMail)) $loginType = 'e_mail';
    else {
    	$loginType = 'login';
    	if (!User_User::isLoginCorrect($this->loginOrEMail)) throw new Exception('Логин заполнен неправильно - допускаются только латинские буквы и цифры');
    }
    $stmt = "SELECT `ID_user`, `pass`, `approved` FROM `user` WHERE `" . $loginType . "` = '" . $this->loginOrEMail . "'";
    $objDB = Service_Database::instance();
    $arrUserFromDB = $objDB->selectOne($stmt);
    if (!$arrUserFromDB) throw new Exception(Service_Translate::getText('Пользователя с таким логином / электронной почтой не существует'));
    if ($this->md5Pass != $arrUserFromDB['pass']) throw new Exception(Service_Translate::getText('Пароль указан неверно'));
    if($this->needApprove) if (!$arrUserFromDB['approved']) throw new Exception(Service_Translate::getText('Вы не подтвердили адрес электронной почты'));

    if ($this->makeSignInSuccess) $this->signInSuccess($arrUserFromDB['ID_user']);
    return $arrUserFromDB['ID_user'];
	}
	
	/**
	 * Вход пользователя по ID бронирования и пинкоду
	 * 
	 */
	private function _signInByBooking() {
		$objBooking = new Booking($this->loginOrEMail);
		$objBooking->find('first', array('conditions' => "`booking`.`id` = '" . intval($this->loginOrEMail) . "'"));
		if (!$objBooking->id) return false; // Заказа нет, дествуем по обычной схеме логина
		if (!$objBooking->getField('ID_user')) throw new Exception(Service_Translate::getText('Для этого заказа не был создан заказ. Свяжитесь со службой поддержки.'));
		if ($objBooking->getField('pincode') != $this->pass) throw new Exception(Service_Translate::getText('Неверно введен PIN'));
		$objUser = new User_User($objBooking->getField('ID_user'));
		//if ($objUser->getField('e_mail') != $objBooking->getField('e_mail')) throw new Exception(Service_Translate::getText('E-mail, указанный в заказе не соответствует E-mail зарегистрированного пользователя.'));
		if ($objUser->getField('is_admin') || $objUser->getField('hotel_owner_level')) throw new Exception(Service_Translate::getText('Вход для администраторов сайта по пинкоду не предусмотрен.'));
		
		
		$this->signInSuccess($objBooking->getField('ID_user'));
		return $objBooking->getField('ID_user'); 
	}
	
	/**
	 * Пользователь вошел - обновляем сессию и данные в БД
	 * 
	 */
	public function signInSuccess($userId) {
		if (!$userId) return false;
		if (in_array($userId, self::$arrBlockedUsers)) throw new Exception(Service_Translate::getText('Аккаунт заблокирован. Свяжитесь с администрацией сайта.'));
		$objSession = User_Session::instance();
		$objSession->signInSuccess($userId); // Запоминаем id пользователя для текущ0его сеанса
    User_User::setLoginTime(); // Обновляем время последнего входа
    User_User::setStatus(2, $userId); // Устанавливаем статус активного сеанса
    $this->updateSaveMeCookie(); // Обновляем куку "Запомнить меня"
	}
	
	/**
	 * Обновляет значение куки 'SaveMe'
	 * 
	 */
	public function updateSaveMeCookie() {
	  if ($this->saveMe) {
    	$objSession = User_Session::instance();
	  	$objUser = $objSession->getUser();
		  $superStr = md5($objUser->getField('e_mail') . $this->superSlovo . $objUser->getField('pass') . $this->superSlovo);
			setcookie("SaveMe" , $superStr , time() + 60480000, W_ABSPATH, DOMAIN);
			setcookie("ID" , $objUser->id , time() + 60480000, W_ABSPATH, DOMAIN);
		}
		else setcookie("SaveMe");
	}
	
  /**
   * Функция автозахода если пользователь ставил галочку "Запомнить меня"
   *
   * @return bool - Запомнили или нет
   */
  public function ifSaveMe() {
  	if (!isset($_COOKIE['SaveMe']) || !isset($_COOKIE['ID'])) return false; // Если у нас нет кук, то заканчиваем
  	
  	$objSession = User_Session::instance();
  	if ($objSession->isLoggedIn()) return false; // Если мы и так залогинены, то закапнчиваем

  	$objDB = Service_Database::instance();
  	//Делаем выборку почты и пароля по ID пользователя
  	$stmt = "SELECT `e_mail`, `login`, `pass` FROM `user` WHERE ID_user='" . $_COOKIE['ID'] . "'";
  	$arMailAndPass = $objDB->selectOne($stmt);
  	if (!$arMailAndPass) return false; // Пользователя с таким ID не существует
  	
  	$eMail = $arMailAndPass['e_mail'];
  	$md5pass = $arMailAndPass['pass'];
  	$superStr = md5($eMail . $this->superSlovo . $md5pass . $this->superSlovo); //Формируем супер строку =)
  	if ($superStr != $_COOKIE['SaveMe']) return false; // Кука фальшивая

  	$this->saveMe = true;
  	$this->signInSuccess($_COOKIE['ID']);
  }

	/**
	 * Получение страницы авторизации facebook
	 *  
	 * @param string $redirectUrl
	 */
	public function getFBAuthorizationPage($redirectUrl) {
		$objSession = User_Session::instance();
    $objSession->__set('state', md5(uniqid(rand(), TRUE))); //CSRF protection
    $dialogUrl = "http://www.facebook.com/dialog/oauth?client_id=" 
      . FACEBOOK_API . "&redirect_uri=" . urlencode($redirectUrl) . "&state="
      . $objSession->__get('state');

     echo("<script> top.location.href='" . $dialogUrl . "'</script>");
	}
	
	/**
	 * Метод авторизовывает пользователя через facebook
	 * 
	 */
	public function authFacebook() {
		$facebook = new User_Facebook(array(
		  'appId'  => FACEBOOK_API,
		  'secret' => FACEBOOK_SECRET,
		));
		$userId = $facebook->getUser();
		//$userProfile = $facebook->api('/me','GET');

		if ($userId) { 
      $objUserAuth = new User_Auth();
			$userInfo = $facebook->api('/' + $userId);
			$objUserAuth->facebookLogin($userInfo);
   }
	}
	
	/**
	 * Получение страницы авторизации facebook
	 *  
	 * @param string $fbUserData
	 */
	public function facebookLogin($fbUserData) {
		if (empty($fbUserData)) return false; // Не передан пользователь FB
		if (!isset($fbUserData['email'])) return false;
    $userId = User_User::getUserIdByFbId($fbUserData['id']);
    if (!$userId) $userId = User_User::getUserIdByEMail($fbUserData['email']);
   
		if ($userId) { // Такой пользователь уже существует в БД
    	if ($this->makeSignInSuccess) $this->signInSuccess($userId);
    	if (!empty($fbUserData['id'])) {
    		$objUser = new User_User($userId);
    		$objUser->updateField('ID_fb', $fbUserData['id']);
    		if (!$objUser->getField('ID_photo')) {
    			$url = 'http://graph.facebook.com/' . $fbUserData['id'] . '/picture?type=large';
    			$objUser->saveAvatarFromUrl($url);
    		}
    		
    	}
    }
    else{
    	$arrFbUserToSave = array();
    	$arrAdditionalFields = array();
    	if (isset($fbUserData['first_name'])) $arrAdditionalFields['first_name'] = $fbUserData['first_name'];
    	if (isset($fbUserData['last_name'])) $arrAdditionalFields['last_name'] = $fbUserData['last_name'];
    	if (isset($fbUserData['hometown']['name'])) $arrAdditionalFields['city'] = $fbUserData['hometown']['name'];
    	if (isset($fbUserData['email'])) $this->eMail = $fbUserData['email']; 
    	if (isset($fbUserData['id'])) $arrAdditionalFields['ID_fb'] = $fbUserData['id']; 
    // [id] => 100001639218711
    // [name] => Dmitri Kouzma
    // [link] => http://www.facebook.com/profile.php?id=100001639218711
    // [hometown][id] => 108131085886116
    // [gender] => male
    // [timezone] => 4
    // [locale] => ru_RU
			$userId = $this->createNewUser('FB_sign_up', $arrAdditionalFields, false);
			if ($userId) { // Если зарегистрировались, то заходим
				if ($this->makeSignInSuccess) $this->signInSuccess($userId);
				if ($this->makeSignInSuccess && !empty($fbUserData['id'])) {
					$url = 'http://graph.facebook.com/' . $fbUserData['id'] . '/picture?type=large';
					$objUser = new User_User($userId);
					$objUser->saveAvatarFromUrl($url);
				}
			}
			elseif (defined('DOMAIN')) { // Если не удалось зарегистрироваться, то убиваем куки
	      setcookie('fbm_' . FACEBOOK_API, null, null, W_ABSPATH, '.' . DOMAIN);
	      setcookie('fbsr_' . FACEBOOK_API, null, null, W_ABSPATH, '.' . DOMAIN);
      }
    }
    return $userId;
	}
	
	/**
	 * Метод авторизовывает пользователя VK
	 * 
	 * @return mixed - false, если не удалось залогиниться и array с member, если удалось
	 */
	public function authVk() {
		$arrVkParams = array();
	  $arrMember = array();
	  $validKeys = array('expire', 'mid', 'secret', 'sid', 'sig');
	  $cookieName = $_COOKIE['vk_app_' . VK_API];
	  if (empty($cookieName)) return false;
    $arrVkParamPairs = explode('&', $cookieName, 10);
    // Считываем все переданные параметры
    foreach ($arrVkParamPairs as $paramPair) {
      $arrParamPair = explode('=', $paramPair, 2);
    	if (!isset($arrParamPair[1])) continue;
      list($key, $value) = $arrParamPair;
      if (empty($key) || empty($value) || !in_array($key, $validKeys)) continue;
      $arrVkParams[$key] = $value;
    }
    foreach ($validKeys as $key) if (!isset($arrVkParams[$key])) return false; // Если какого-то из параметров не хватает, то выходим
    ksort($arrVkParams);

    // Составляем подпись
    $sign = '';
    foreach ($arrVkParams as $key => $value) {
      if ($key != 'sig') $sign .= ($key . '=' . $value);
    }
    $sign .= VK_SECRET;
    $signMd5 = md5($sign);
    if ($arrVkParams['sig'] == $signMd5 && $arrVkParams['expire'] > time()) {
	    $arrMember = array(
	      'id' => intval($arrVkParams['mid']),
	      'secret' => $arrVkParams['secret'],
	      'sid' => $arrVkParams['sid']
		  );
		  if (!empty($_COOKIE['vk_first_name'])) $arrMember['first_name'] = $_COOKIE['vk_first_name'];
		  if (!empty($_COOKIE['vk_last_name'])) $arrMember['last_name'] = $_COOKIE['vk_last_name'];
		  if (!empty($_COOKIE['vk_photo_big'])) $arrMember['photo_big'] = $_COOKIE['vk_photo_big'];
		  
		  $arrMember['ID_user'] = $this->vkLogin($arrMember);
    }
	  return $arrMember;    
	}
	/**
	 * Метод возвращает ID пользователя VK
	 */
	public function getVkId() {
		if (!isset($_COOKIE['vk_app_' . VK_API])) return false;
	  $cookieName = $_COOKIE['vk_app_' . VK_API];
	  if (empty($cookieName)) return false;
    $arrVkParams = preg_match('#&mid=([0-9]+)&#', $cookieName, $arrMatches);
    if (!isset($arrMatches[1])) return false; 
    return $arrMatches[1];
	}
	
	/**
	 * Осуществляет вход посредством контакта
	 * 
	 * @param array $arrVkData - массив с данными контакта
	 */
	public function vkLogin(array $arrVkData) {

    $userId = User_User::getUserIdByVkId($arrVkData['id']);
    if (!$userId && isset($arrVkData['e_mail'])) $userId = User_User::getUserIdByEMail($arrVkData['e_mail']);
    if ($userId) { // Такой пользователь уже существует в БД
    	$objOurUser = new User_User($userId);
     	if (isset($arrVkData['first_name']) && (!$objOurUser->getField('first_name') || is_numeric($objOurUser->getField('first_name'))) ) $objOurUser->updateField('first_name', $arrVkData['first_name']);
     	if (isset($arrVkData['last_name']) && !$objOurUser->getField('last_name')) $objOurUser->updateField('last_name', $arrVkData['last_name']);
     	if (isset($arrVkData['e_mail']) && !$objOurUser->getField('e_mail')) $objOurUser->updateField('e_mail', $arrVkData['e_mail']);
     	if (isset($arrVkData['ID_vk']) && !$objOurUser->getField('ID_vk')) $objOurUser->updateField('ID_vk', $arrVkData['ID_vk']);
   		if ($this->makeSignInSuccess) {
   			$this->signInSuccess($userId);
   			if (isset($arrVkData['photo_big']) && !$objOurUser->getField('ID_photo')) {   				
   				$objOurUser->saveAvatarFromUrl($arrVkData['photo_big']);
   			}
   		}
   	}
   	else{ // новый пользователь
    	$arrAdditionalFields = array(
    		'ID_vk' => $arrVkData['id'],
    		'first_name' => $arrVkData['id']
    	);
    	if (isset($arrVkData['first_name'])) $arrAdditionalFields['first_name'] = $arrVkData['first_name']; 
    	if (isset($arrVkData['last_name'])) $arrAdditionalFields['last_name'] = $arrVkData['last_name']; 
    	if (isset($arrVkData['e_mail'])) $arrAdditionalFields['e_mail'] = $arrVkData['e_mail']; 
   		$userId = $this->createNewUserWithoutMail('VK_sign_up', $arrAdditionalFields);
			if ($userId) { // Если зарегистрировались, то заходим
				if ($this->makeSignInSuccess) {
					$this->signInSuccess($userId);
					if (isset($arrVkData['photo_big'])) {
						$objUser = new User_User($userId);
						$objUser->saveAvatarFromUrl($arrVkData['photo_big']);
					}
				}
			}
   	}
   	return $userId;
		
	}
	
  /**
   * Восстновление пароля
   *
   */
	public function recoveryPass() {
	  Service_Debug::setDebug();
	  $objSession = User_Session::instance();
	  $exCeptionClassName = 'User_User';
    
    if (Service_Mail::isEMailCorrect($this->loginOrEMail)) {
    	if (!User_User::isEMailExist($this->loginOrEMail)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'e_mail', Service_Translate::getText('Пользователя с таким адресом электронной почты не существует.'));
    }
    else Exception_ActiveRecord::addObjectException($exCeptionClassName, 'e_mail', Service_Translate::getText('Электронная почта заполнена неверно'));
    
    if ($this->checkKaptcha && (empty($this->kaptcha) || ($objSession->__get('keystring') != $this->kaptcha))) {
      Exception_ActiveRecord::addObjectException($exCeptionClassName, 'kaptcha', Service_Translate::getText('Неверный код подтверждения'));
    }

    if (Exception_ActiveRecord::isExceptionOfModelExists($exCeptionClassName)) return false;

		$newPass = Service_Generate::generatePassword();
    $objDB = Service_Database::instance();//Получаем подключение к БД

    $objDB->update('user', array('pass' => md5($newPass)), array('e_mail' => $this->loginOrEMail));
    $objUser = new User_User(User_User::getUserIdByEMail($this->loginOrEMail));
    $objNotification = new User_Notification();
    $eMail = Service_Amail::getUserEMail($objUser->tableFields);
    $eMail = empty($eMail) ? $this->loginOrEMail : $eMail;
    $objNotification->recovery($eMail, $newPass);
    //Service_Amail::getUserEMail($objUser->tableFields)
    return $newPass;
  }
  
  /**
   * Смена пароля
   * 
   * @param string $oldPass - старый пароль
   * @param string $newPass - новый пароль
   * @param string $newPassConfirmation - подтверждение нового пароля
   */
	public function changePassword($oldPass, $newPass, $newPassConfirmation) {
	  Service_Debug::setDebug();
	  $objSession = User_Session::instance();
	  $exCeptionClassName = 'User_User';
	  
	  $objUser = User_User::getUser();
	  if (!$objUser->id) {
	  	Exception_ActiveRecord::addObjectException($exCeptionClassName, 'all', Service_Translate::getText('Только зарегистрированные пользователи могут менять пароль'));
	  	return false; 
	  }
	  if ($objUser->getField('pass') != md5($oldPass)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'pass', Service_Translate::getText('Неверно указан старый пароль'));
	  if (Service_Function::utf8_strlen($newPass) < 6) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'new_pass', Service_Translate::getText('Пароль введен неверно, должен состоять из 6-128 символов'));
	  elseif ($newPass != $newPassConfirmation) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'new_pass_confirmation', Service_Translate::getText('Пароль и его подтверждение не совпадают'));
	  if (Exception_ActiveRecord::isExceptionOfModelExists($exCeptionClassName)) return false;
	  
	  $objUser->updateField('pass', md5($newPass));
	  return true;
	}
	
  /**
   * Смена пароля - установка пароля конкретному юзеру - доступно только администратору
   * 
   * @param string $newPass - новый пароль
   * @param string $newPassConfirmation - подтверждение нового пароля
   * @param string $userId - если пароль меняется другому пользователю
   */
	public function setNewPasswordToUser($newPass, $newPassConfirmation, $userId = null) {
	  Service_Debug::setDebug();
	  $objSession = User_Session::instance();
	  $exCeptionClassName = 'User_User';
	  if (!$userId) return false;
	  if (!SimpleAdministration::isAdmin()) {
	  	Exception_ActiveRecord::addObjectException($exCeptionClassName, 'all', Service_Translate::getText('Только администратор может установить новый пароль пользователю'));
	  	return false;
	  }
	  $objUser = new User_User($userId);
	  if (!$objUser->id) {
	  	Exception_ActiveRecord::addObjectException($exCeptionClassName, 'all', Service_Translate::getText('Такого пользователя не существует.'));
	  }

	  if (Service_Function::utf8_strlen($newPass) < 6) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'new_pass', Service_Translate::getText('Пароль введен неверно, должен состоять из 6-128 символов'));
	  elseif ($newPass != $newPassConfirmation) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'new_pass_confirmation', Service_Translate::getText('Пароль и его подтверждение не совпадают'));
	  if (Exception_ActiveRecord::isExceptionOfModelExists($exCeptionClassName)) return false;
	  
	  $objUser->updateField('pass', md5($newPass));
	  return true;
	}
  
	/**
	 * Создает нового пользователя
	 * 
	 * @param string $exCeptionClassName - название класса, которому будут соответствовать исключения
	 * @param array $arrAdditionalFields - массив дополнительных параметров, которые будут сохранены в таблицу user
	 * @param string $notificationFunction - отправлять ли оповещения. Если true, то будет отправлено стандартное. Так же моно указать, что требуется отправить особое оповещение
	 * 
	 * @return int - идентификатор пользователя
	 */
	public function createNewUser($exCeptionClassName = 'User_User', $arrAdditionalFields = array(), $notificationFunction = 'createAccount') {
		if ($this->useLogin) {
			if (!User_User::isLoginCorrect($this->login)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'login', Service_Translate::getText('Логин заполнен неправильно - допускаются только латинские буквы и цифры'));
			//throw new Exception('Логин заполнен неправильно - допускаются только латинские буквы и цифры');
			elseif ($this->isLoginExist($this->login)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'login', Service_Translate::getText('Пользователь с таким логином уже существует'));
			//throw new Exception('Пользователь с таким логином уже существует');
		}
		if (!Service_Mail::isEMailCorrect($this->eMail)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'e_mail', Service_Translate::getText('Электронная почта заполнена неверно'));
		//throw new Exception('Адрес электронной почты заполнен неверно');
		elseif (User_User::isEMailExist($this->eMail))   Exception_ActiveRecord::addObjectException($exCeptionClassName, 'e_mail', Service_Translate::getText('Эта электронная почта уже используется'));
		//throw new Exception('Пользователь с таким адресом электронной почты уже существует');

		if (Exception_ActiveRecord::isExceptionOfModelExists($exCeptionClassName)) return false;
		
		$objDB = Service_Database::instance();//Получаем подключение к БД
		if (!$this->md5Pass) $this->setPass(Service_Generate::generatePassword()); // Генерируем пароль, если он еще не создан
		
		// Запрос на вставку в БД нового юзера
		$stmt ="INSERT INTO `user` (";
		  $stmt .= "`login`, `e_mail`, `pass`";
		  if($arrAdditionalFields) {
		  	foreach ($arrAdditionalFields as $additionalFieldName => $additionalFieldValue) {
		  		$stmt .= ', `' . $additionalFieldName . '`';
		  	}
		  }
		$stmt .= ") 
		VALUES ('";
		  $stmt .= $this->login . "', '" . $this->eMail . "', '" . $this->md5Pass . "'";
			  if ($arrAdditionalFields) {
		  	foreach ($arrAdditionalFields as $additionalFieldName => $additionalFieldValue) {
		  		$stmt .= ", '" . Service_Escape::absolutelyEscapeString($additionalFieldValue) . "'";
		  	}
		  }
		$stmt .= ");";
		$userId = $objDB->queryInsertAutoincrement($stmt);
		if (!empty($notificationFunction)) {
			$objNotification = new User_Notification();
			$arrAdditionalFields['e_mail'] = $this->eMail;
			$eMail = Service_Amail::getUserEMail($arrAdditionalFields);
			if (!isset($arrAdditionalFields['first_name'])) $arrAdditionalFields['first_name'] = '';
			if (!isset($arrAdditionalFields['last_name'])) $arrAdditionalFields['last_name'] = '';
			$userName = View_User::getUserName($arrAdditionalFields);
			$objNotification->createAccount($eMail, $this->login, $this->pass, $userName);			
		}
		return $userId;
	}
	
	/**
	 * Создает нового пользователя/ Метод подразумевает, что у пользователя нет почты
	 * 
	 * @param string $exCeptionClassName - название класса, которому будут соответствовать исключения
	 * @param array $arrAdditionalFields - массив дополнительных параметров, которые будут сохранены в таблицу user
	 * 
	 * @return int - идентификатор пользователя
	 */
	public function createNewUserWithoutMail($exCeptionClassName = 'User_User', $arrAdditionalFields = array()) {
		if ($this->useLogin) {
			if (!User_User::isLoginCorrect($this->login)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'login', Service_Translate::getText('Логин заполнен неправильно - допускаются только латинские буквы и цифры'));
			//throw new Exception('Логин заполнен неправильно - допускаются только латинские буквы и цифры');
			elseif ($this->isLoginExist($this->login)) Exception_ActiveRecord::addObjectException($exCeptionClassName, 'login', Service_Translate::getText('Пользователь с таким логином уже существует'));
			//throw new Exception('Пользователь с таким логином уже существует');
		}

		if (Exception_ActiveRecord::isExceptionOfModelExists($exCeptionClassName)) return false;
		
		$objDB = Service_Database::instance();//Получаем подключение к БД
		if (!$this->md5Pass) $this->setPass(Service_Generate::generatePassword()); // Генерируем пароль, если он еще не создан
		
		// Запрос на вставку в БД нового юзера
		$stmt ="INSERT INTO `user` (";
		  $stmt .= "`login`, `pass`";
		  if($arrAdditionalFields) {
		  	foreach ($arrAdditionalFields as $additionalFieldName => $additionalFieldValue) {
		  		$stmt .= ', `' . $additionalFieldName . '`';
		  	}
		  }
		$stmt .= ") 
		VALUES ('";
		  $stmt .= $this->login . "', '" . $this->md5Pass . "'";
			  if ($arrAdditionalFields) {
		  	foreach ($arrAdditionalFields as $additionalFieldName => $additionalFieldValue) {
		  		$stmt .= ", '" . Service_Escape::absolutelyEscapeString($additionalFieldValue) . "'";
		  	}
		  }
		$stmt .= ");";
		$userId = $objDB->queryInsertAutoincrement($stmt);
		return $userId;
	}
  

	/**
	 *===============================================================
	 * Ниже ФУНКЦИИ, которые перенесены из User_Session, но не обновлялись
	 *===============================================================
	 */
	

	public function loginByOpenId($openId) {
    $objDB = Service_Database::instance();//Получаем подключение к БД
    $stmt = "SELECT `ID_user`, `login`, `pass` FROM `user` WHERE `open_id` = '" . $openId . "'";  //формируем SQL-запрос
    $arrUser = $objDB->select($stmt);
    if (sizeof($arrUser) > 0) {
    	$objSession = User_Session::instance();
			$ID_user = User_User::logIn($arrUser[0]['login'], $arrUser[0]['pass']);
			if ($ID_user) {//Если есть совпадения (логин+пароль)
				$this->ID_user = $ID_user;//Запоминаем id пользователя
				$this->logged_in = true;//Активируем сеанс
				//Обновляем БД
				//Устанавливаем статус активного сеанса, запоминаем id пользователя для текущ0его сеанса
				$this->_baseLogInSession();
				User_User::setLoginTime();
				User_User::addVisit();
				User_User::setStatus(2, $this->getUserId());
				$this->moveTo(W_ABSPATH);
			}
			else { // Если НЕТ совпадений (логин+пароль)
				return false;
			}
    }
	}


	
	/**
	 * Вход или регистрация по данным полученным с Facebook
	 *
	 */
	public function getFBCookieIfSet() {
		$cookie = Service_Function::getFacebookCookie(FB_APP_ID, FB_APP_SECRET);
		if (!empty($cookie['access_token'])) {
			$arrUser = json_decode(@file_get_contents('https://graph.facebook.com/me?access_token=' . $cookie['access_token']));
			if ($arrUser->id) {
				if (!$this->facebookLogin_($arrUser->email)) {
					$objDB = Service_Database::instance();
					if (!empty($arrUser->link) && !User_User::isLoginExist(str_replace('http://www.facebook.com/', '', $arrUser->link)) && !preg_match('/=/', $arrUser->link)) {
					  $login = str_replace('http://www.facebook.com/', '', $arrUser->link);
					}
					elseif (!User_User::isLoginExist(str_replace(' ', '', $arrUser->name))) {
						$login = str_replace(' ', '', $arrUser->name);
					}
					else $login = $arrUser->id;
					$stmt = "INSERT INTO user (e_mail, login, first_name, last_name, pass, approved) VALUES ('" . $arrUser->email . "', '" . $login . "', '" . $arrUser->first_name . "', '" . $arrUser->last_name . "', '123456', 1)";
					$objDB->query($stmt);
					$ID_user = User_User::login($arrUser->email, '123456');
					if ($ID_user) {//Если есть совпадения (логин+пароль)
						$this->ID_user = $ID_user;//Запоминаем id пользователя
						$this->logged_in = true;//Активируем сеанс
						//Обновляем БД
						//Устанавливаем статус активного сеанса, запоминаем id пользователя для текущ0его сеанса
						$this->_baseLogInSession();
						User_User::setLoginTime();
						User_User::addVisit();
						User_User::setStatus(2, $this->getUserId());
					}
					if (empty($_SERVER['HTTP_REFERER'])) $this->moveTo(W_ABSPATH);
					else $this->moveTo(str_replace('fblogin=1', '', $_SERVER['HTTP_REFERER']));
				}
			}
		}
	}
	
  /**
   * Функция автозахода если пользователь ставил галочку "Запомнить меня"
   *
   * @return bool - Запомнили или нет
   */
  public function facebookLogin_($email) {
  	if ($this->logged_in == false) { //Если сеанс не активен
			$objDB = Service_Database::instance();
			//Делаем выборку майла и пароля по ИД
			$strUserAndPass = 'SELECT `e_mail`, `login`, `pass`, `approved` FROM `user` WHERE e_mail="' . $email . '"';
			$arUserAndPass = $objDB->select($strUserAndPass);
			if (isset($arUserAndPass[0])) { //Если есть совпадение
				if (!$arUserAndPass[0]['approved']) {
					$stmt = 'UPDATE `user` SET `approved` = 1 WHERE e_mail="' . $email . '"';
					$objDB->query($strUserAndPass);
				}
				//Заново логинимся, все довольны =)
				$ID_user = User_User::logIn($arUserAndPass[0]['login'], $arUserAndPass[0]['pass']);
				if ($ID_user) {//Если есть совпадения (логин+пароль)
					$this->ID_user = $ID_user;//Запоминаем id пользователя
					$this->logged_in = true;//Активируем сеанс
					//Обновляем БД
					//Устанавливаем статус активного сеанса, запоминаем id пользователя для текущ0его сеанса
					$this->_baseLogInSession();
					User_User::setLoginTime();
					User_User::addVisit();
					User_User::setStatus(2, $this->getUserId());
					return true;
				}
				else { // Если НЕТ совпадений (логин+пароль)
					return false;
				}
			}
		}
  }
  
	/**
	 *===============================================================
	 * МЕТОДЫ НАПИСАННЫЕ ДЛЯ ТЕСТА
	 *===============================================================
	 */
	
	/**
	 * Получение данных о пользователе от facebook
	 * 
	 * @param string $accessToken
	 */
	public function getFBUserData($accessToken) {
     $graph_url = "https://graph.facebook.com/me?access_token=" 
       . $accessToken;
     return json_decode(file_get_contents($graph_url));
	}
	
	/**
	 * Получение токена доступа к facebook
	 *  
	 * @param string $redirectUrl
	 * @param string $code
	 */
	public function getFBAccessToken($redirectUrl, $code) {
   	$tokenUrl = "https://graph.facebook.com/oauth/access_token?"
       . "client_id=" . FACEBOOK_API . "&redirect_uri=" . urlencode($redirectUrl)
       . "&client_secret=" . FACEBOOK_SECRET . "&code=" . $code;

     $response = @file_get_contents($tokenUrl);
     $params = null;
     parse_str($response, $params);
     if (isset($params['access_token'])) return $params['access_token'];
     else { // Какие-то проблемы ;)
     	return null;
     }
	}
	
	/**
	 * Входим на сайт по токену 
	 * 
	 * @param string $strEMail - E-Mail пользователя
	 * @param string $token - токен для входа
	 */
	public function loginByToken($strEMail, $token) {
		if (User_User::getId()) return false;
		$userId = User_User::getUserIdByEMail($strEMail);
		if (!$userId) return false;
		$bdToken = $this->getToken($userId);
		if ($token == $bdToken) { // Если токен верный то выполняем вход
			$this->signInSuccess($userId);
			return true;
		}
		return false;
	}
	
	/**
	 * Возвращает токен для входа для пользователя
	 * 
	 * @param int $userId - ID пользователя
	 * 
	 * @return string - токен для пользователя
	 */
	public static function getToken($userId) {
		$objUser = new User_User($userId);
		if (!$objUser->id) return false;
		$token = md5($objUser->getField('e_mail') . '_token_' . $objUser->getField('pass') . $objUser->id);
		return $token;
	}
	
	/**
	 * Действия, производящиеся на каждой странице, связанные с авторизацией
	 * Автологины, хиты сессии и пр.
	 * 
	 */
	public static function everyPageAuthorizationActions() {
		if (Service_Function::spiderDetect()) return false; // Не сохраняем ботов
		$objSession = User_Session::instance(); //Создаем сессию
		$objSession->impress();                 //Обновляем последнее обращение сессии
		$objAuth = new User_Auth();
		$objAuth->ifSaveMe();
		// Автоизация из соц. сетей
		Service_Function::unsetCookie('need_e_mail');
		if (!$objSession->isLoggedIn()) {
			// Авторизация VK
			if (!empty($_COOKIE['vk_app_' . VK_API])) {
				$vkId = $objAuth->getVkId();
				$userId = User_User::getUserIdByVkId($vkId);
				$objUser = new User_User($userId);
				if ($objUser->id) {
					if (!$objUser->getField('e_mail')) { // Если не заполнено мыло
						$_COOKIE['need_e_mail'] = 1;
					}
					else $arrMember = $objAuth->authVk();
				}
				else {
					$arrMember = $objAuth->authVk();
					$_COOKIE['need_e_mail'] = 1;
					$objSession = User_Session::instance();
					$objSession->logOut(false, false);
				}
			}
			
			// Авторизация Facebook
			if (!empty($_COOKIE['fbsr_' . FACEBOOK_API]) || !empty($_COOKIE['fbm_' . FACEBOOK_API])) {
				$objAuth->authFacebook();
			}
		}		
		
		
		// Авторизация из письма
		$eMail = Service_Escape::requestDataStringRegExp('e_mail', '/^[\.\-_A-Za-z0-9]+?@[\.\-A-Za-z0-9]+?\.[A-Za-z0-9]{2,6}$/', 'GET');
		$token = Service_Escape::requestDataString('auth_token', 'GET');
		if ($eMail && $token) {
			$objAuth = new User_Auth();
			$objSession->logOut();
			$objAuth->loginByToken($eMail, $token);
		}
	}
	
	/**
	 * Метод авторизовывает пользователя посредством googlePlus по коду аутентификации
	 */
	function authGooglePlus($code, $gPlusClientId = GOOGLE_PLUS_CLIENT_ID, $gPlusClientSecret = GOOGLE_PLUS_CLIENT_SECRET) {
		if (empty($code)) return false;
		require_once 'google_api_php_client/src/Google_Client.php';
		require_once 'google_api_php_client/src/contrib/Google_PlusService.php';
			
		$client = new Google_Client();
		$client->setApplicationName("Hotels.ru");
		$client->setRedirectUri('postmessage');
		$client->setClientId($gPlusClientId);
		$client->setClientSecret($gPlusClientSecret);
		$plus = new Google_PlusService($client);
			
		try {
			// Получаем token
			$client->authenticate($code);
			
			$token = json_decode($client->getAccessToken());
			

			// Получаем информацию по текущему коду. В нем хранится mail пользователя
			// echo $token->access_token;;
			$reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' . $token->access_token;
			$req = new Google_HttpRequest($reqUrl);
			$tokenInfo = json_decode(
					$client::getIo()->authenticatedRequest($req)->getResponseBody()
			);
			
			// Если в данных токена есть ошибка, отмените действие.
			if (!empty($tokenInfo->error)) {
		  	throw new Exception('Token data error.');
		  }
		  // Убедитесь в том, что токен получен для этого приложения.
		  if ($tokenInfo->audience != $gPlusClientId) {
		    throw new Exception('Token\'s client ID does not match app\'s.');
		  }

			// Получаем информацию о пользователе
			$arrGooglePlusUserData = $plus->people->get('me');
			//echo '<pre>';
			//print_r($tokenInfo);
			//echo '</pre>';
			$arrGooglePlusUserData['e_mail'] = $tokenInfo->email;;
			return $this->googlePlusLogin($arrGooglePlusUserData);
			//echo '<pre>';
			//print_r($arrGooglePlusUserData);
			//echo '</pre>';
		}
		catch (Exception $e) {
			header('Content-Type: application/json');
			echo @json_encode($e);
			die;
			return false;
			// echo '<pre>';
			// print_r($e);
			// echo '</pre>';
		}
	}
	

	/**
	 * Окончательная авторизация пользователя уже после проверки гуглом с сохранением данных из гугла
	 *
	 * @param array $googlePlusUserData
	 */
	public function googlePlusLogin(array $arrGooglePlusUserData) {
		if (empty($arrGooglePlusUserData)) return false; // Не передан пользователь G+
		if (!isset($arrGooglePlusUserData['e_mail'])) return false;
		$googlePlusId = $arrGooglePlusUserData['id'];
	
		$userId = User_User::getUserIdByGooglePlusId($arrGooglePlusUserData['id']);
		if (!$userId) $userId = User_User::getUserIdByEMail($arrGooglePlusUserData['e_mail']);
		if ($userId) { // Такой пользователь уже существует в БД
			if ($this->makeSignInSuccess) $this->signInSuccess($userId);
			if (!empty($arrGooglePlusUserData['id'])) {
				// Аватарка для google plus хранится в $arrGooglePlusUserData['image']['url']
				$objUser = new User_User($userId);
				$objUser->updateField('ID_google_plus', $arrGooglePlusUserData['id']);
				if (!$objUser->getField('ID_photo') && isset($arrGooglePlusUserData['image']['url'])) {
					$objUser->saveAvatarFromUrl($arrGooglePlusUserData['image']['url']);
				}
			}
		}
		else{
			$arrAdditionalFields = array();
			if (isset($arrGooglePlusUserData['name']['givenName'])) $arrAdditionalFields['first_name'] = $arrGooglePlusUserData['name']['givenName'];
			if (isset($arrGooglePlusUserData['name']['familyName'])) $arrAdditionalFields['last_name'] = $arrGooglePlusUserData['name']['familyName'];
			if (isset($arrGooglePlusUserData['e_mail'])) $this->eMail = $arrGooglePlusUserData['e_mail'];
			if (isset($arrGooglePlusUserData['id'])) $arrAdditionalFields['ID_google_plus'] = $arrGooglePlusUserData['id'];
			// [id] => 105020561206163333510
			// [gender] => male
			// [url] => https://plus.google.com/105020561206163333510
			// [image] => Array([url] => https://lh3.googleusercontent.com/-0VroixoqErA/AAAAAAAAAAI/AAAAAAAAAIU/D9Dp6Q_55Wk/photo.jpg?sz=50)
			// [displayName] => Дмитрий Кузьма
			// [isPlusUser] => 1
			// [language] => ru
			// [ageRange] => Array([min] => 21)
				
			$userId = $this->createNewUser('google_plus_sign_up', $arrAdditionalFields, false);
			if ($userId) { // Если зарегистрировались, то заходим
				if ($this->makeSignInSuccess) $this->signInSuccess($userId);
				$objUser = new User_User($userId);
				if (isset($arrGooglePlusUserData['image']['url'])) {
					$objUser->saveAvatarFromUrl($arrGooglePlusUserData['image']['url']);
				}
			}
		}
		return $userId;
	}
	


	
}
?>